package com.zhou.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Map;
import java.util.Objects;

/**
 * 脚本引擎计算工具
 * @author lang.zhou
 * @date 2022/4/6 17:23
 */
@Slf4j
public class GroovyEngine {
    public static ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");

    public static ThreadLocal<Bindings> bindingMap = new ThreadLocal<>();

    private static GroovyEngine groovyEngine = new GroovyEngine();

    private GroovyEngine() {
    }
    public static GroovyEngine getInstance(){
        return groovyEngine;
    }

    /**
     * 加载外部脚本函数
     * @param f     文件路径，可为文件夹
     */
    public static void loadExpandFile(File f){
        if(f.isDirectory()){
            for (File file : Objects.requireNonNull(f.listFiles())) {
                loadExpandFile(file);
            }
        }else if(f.isFile()){
            FileReader reader = null;
            try {
                reader = new FileReader(f);
                engine.eval(reader);
            } catch (ScriptException | FileNotFoundException e) {
                log.error("["+f+"]加载失败. ",e);
            }finally {
                IOUtils.closeQuietly(reader);
            }
        }else{
            log.warn("加载groovy脚本失败. ["+f+"]不存在");
        }
    }

    /**
     * 初始化一段脚本
     */
    public static void loadExternalScript(String script){
        try {
            engine.eval(script);
        } catch (ScriptException e) {
            log.error("加载Groovy脚本失败",e);
        }
    }


    /**
     * 创建变量作用域
     */
    private Bindings getEngineBinding(){
        Bindings bindings = bindingMap.get();
        if(bindings == null){
            bindingMap.set(bindings = engine.createBindings());
        }
        return bindings;
    }

    public void clear(){
        getEngineBinding().clear();
    }
    public static void cleanScope(){
        bindingMap.remove();
    }
    public void put(String key, Object value){
        getEngineBinding().put(key,value);
    }
    public void putAll(Map<String,Object> map){
        getEngineBinding().putAll(map);
    }
    public Object eval(String exp) throws Exception {
        return engine.eval(exp,getEngineBinding());
    }

    public boolean valid(String exp) throws Exception {
        return (boolean)eval(exp);
    }
}
